﻿using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
/*public class ZoomLevel
{
public float Height = 1.0f;
public float PortraitHeight = 1.0f;
public float FieldOfView = 60.0f;
}*/
public class CameraGesture : MonoBehaviour ,IInput
{
    public interface IGestureHandler
    {
        void OnEnter();
        void OnExit();
        bool IsCameraOn();
        void OnPointDown(Vector2 screenPos, Vector3 worldPos);
        void OnPointUp(Vector2 screenPos, Vector3 worldPos);
        void OnTap(Vector2 screenPos, Vector3 worldPos);
        void OnZoom(int zoomLevel);
        bool OnDrag(Vector2 screenPos);
    }

    public static Vector3 nullPointer = new Vector3(-99999, -99999, -99999);
    // *********************************
    public ZoomLevel[] ZoomLevels;
    public float maxHeight = 12;
    public float minHeight = 5;
    public Camera mCamera = null;
    //
    private Transform cameraTransform;
    private Ray ray;
    private RaycastHit rayHit;
    private Vector3 pointerDownPos = nullPointer;
    private float perLevelHeight = 0;
    private float zoomFactor = 10;
    private float lastPointerTime = 0;
    private float factor = 1;
    private int _curZoom = 2;
    private int lastZoom = 2;
    private bool isControlled = false;
    private bool isZooming = false;

    static CameraGesture _Instance;
    private IGestureHandler mHandler;
    private IInputProvider mInputProvider;
    public static CameraGesture Instance
    {
        get {
            return _Instance;
        }
    }
    public int CurrentZoom
    {
        get {
            return _curZoom;
        }
    }
    public ZoomLevel CurZoomLevel
    {
        get
        {
            return ZoomLevels[_curZoom];
        }
    }
    void Awake()
    {
        if(_Instance)
        {
            _Instance.DOKill();
            if (_Instance.gameObject != gameObject)
            {
                throw new System.Exception("你怎么能构造两个这玩意");
            }
            else
                GameObject.Destroy(_Instance);
        }
        _Instance = this;
#if UNITY_EDITOR || UNITY_STANDALONE
        mInputProvider = new MouseProvider(this);
#else
        mInputProvider = new TouchProvider(this);
#endif
        if (!mCamera)
            mCamera = gameObject.GetComponent<Camera>();
        cameraTransform = mCamera.transform;

        perLevelHeight = (maxHeight - minHeight) / 7.0f;
        ZoomLevels = new ZoomLevel[7];
        for (int i = 0; i < 7; i++)
        {
            ZoomLevel zl = new ZoomLevel();
            zl.Height = minHeight + i * perLevelHeight;
            ZoomLevels[i] = zl;
        }
    }
    public void Enter(IGestureHandler handler)
    {
        mHandler = handler;
        mCamera.enabled = true;
        isZooming = false;
        UpdateShadowDistance();
        UpdateMoveSpeedFactor();
        handler.OnEnter();
        ResetBoundParams();
    }
    public void Exit()
    {
        mHandler.OnExit();
        mHandler = null;
        mCamera.enabled = false;
        isZooming = false;
    } 

    public void UpdateShadowDistance()
    {
        float diffValue = 0;
        float shadowDistance = 0;
        shadowDistance = perLevelHeight * _curZoom * 2 + 10;
        QualitySettings.shadowDistance = shadowDistance + diffValue;
    }
    
    private IEnumerator DelayReset(float delay = 0.1f)
    {
        yield return new WaitForSeconds(delay);
        Input.ResetInputAxes();
    }

    public void ManualCameraViewTo(ZoomLevel zl, float duration, System.Action<object> complete = null, object param = null)
    {
        isControlled = true;
        ChangeCameraViewTo(zl,duration,complete,param);
    }
    private void ChangeCameraViewTo(ZoomLevel zl, float duration, System.Action<object> complete = null, object param = null)
    {
        DOTween.Kill(cameraTransform);
        StopCoroutine("MoveCameraCoroutine");
        StartCoroutine("MoveCameraCoroutine", new Dictionary<string, object>() { { "zl", zl }, { "duration", duration }, { "complete", complete }, { "param", param } });
        isZoomEffecting = true;
    }
    bool isZoomEffecting = false;
    private IEnumerator MoveCameraCoroutine(Dictionary<string, object> argvDic)
    {
        ZoomLevel zl = argvDic["zl"] as ZoomLevel;
        float duration = (float)argvDic["duration"];
        System.Action<object> complete = argvDic["complete"] as System.Action<object>;
        object param = argvDic["param"];
        yield return new WaitForEndOfFrame();
        Vector3 destP = (zl.Height - cameraTransform.localPosition.y) / cameraTransform.forward.y * cameraTransform.forward + cameraTransform.localPosition;
        destP = CheckBounds(destP);
        cameraTransform.DOLocalMove(destP, duration).SetEase(Ease.Linear).OnComplete(() =>
        {
            isZoomEffecting = false;
            isControlled = false;
            if (null != complete) complete(param);
        });
    }

    /// <summary>
    /// 地图边界 （minx,maxx,minz,maxz）
    /// </summary>
    Vector4 MapBounds = new Vector4(0,0,0,0);
    /// <summary>
    /// 计算的边界delta值 （disTop,disBottom,disX）
    /// </summary>
    Vector3 dis_TopY_BottomY_X = new Vector3(0, 0, 0);
    void ResetBoundParams()
    {
        float fovDegree = mCamera.fieldOfView;
        float rotateDegree = 90 - cameraTransform.eulerAngles.x;
        dis_TopY_BottomY_X.x = cameraTransform.position.y * (float)Mathf.Tan((rotateDegree + fovDegree / 2) * Mathf.Deg2Rad);
        dis_TopY_BottomY_X.y = -cameraTransform.position.y * (float)Mathf.Tan((rotateDegree - fovDegree / 2) * Mathf.Deg2Rad);

        fovDegree = Screen.width / Screen.height * fovDegree;
        dis_TopY_BottomY_X.z = cameraTransform.position.y * (float)Mathf.Tan(fovDegree * Mathf.Deg2Rad);
        
    }

    private Vector3 CheckBounds(Vector3 pos)
    {
        float top = MapBounds.w + paddingEdge.y;
        float bottom = MapBounds.z - paddingEdge.z;
        float left = MapBounds.x - paddingEdge.x;
        float right = MapBounds.y + paddingEdge.x;

        if (pos.z + dis_TopY_BottomY_X.x > top)
            pos.z = top - dis_TopY_BottomY_X.x;
        if (pos.z - dis_TopY_BottomY_X.y < bottom)
            pos.z = bottom + dis_TopY_BottomY_X.y;
        if (pos.x - dis_TopY_BottomY_X.z < left)
            pos.x = left + dis_TopY_BottomY_X.z;
        if (pos.x + dis_TopY_BottomY_X.z > right)
            pos.x = right - dis_TopY_BottomY_X.z;
        return pos;
    }

    private void SetZoomLevel()
    {
        UpdateShadowDistance();
        UpdateMoveSpeedFactor();
    }
    private void UpdateMoveSpeedFactor()
    {//更新移动屏幕比例speed
        Vector3 center = mCamera.ViewportToScreenPoint(new Vector3(0.5f, 0.5f));
        Ray ray = mCamera.ScreenPointToRay(center);
        if (Physics.Raycast(ray, out rayHit))
        {
            center = rayHit.point;
            Vector3 v1 = mCamera.WorldToScreenPoint(center + Vector3.left);
            Vector3 v2 = mCamera.WorldToScreenPoint(center + Vector3.right);
            factor = Vector3.Distance(v1, v2);// * Time.deltaTime;
            factor = 2 / factor; // * 5;
            if (MapBounds.x < 1)
            {
                BoxCollider mapCollider = rayHit.collider as BoxCollider;
                MapBounds = new Vector4(
                    mapCollider.bounds.min.x,
                    mapCollider.bounds.max.x,
                    mapCollider.bounds.min.z,
                    mapCollider.bounds.max.z);
            }
        }
    } 
    public void OnPointerDown(Vector2 pos)
    {
        ray = mCamera.ScreenPointToRay(pos);
        if (Physics.Raycast(ray, out rayHit))
        {
            Vector3 hitPos = rayHit.point;
            mHandler.OnPointDown(pos, hitPos);
        }
        else
            mHandler.OnPointDown(pos, nullPointer);
        pointerDownPos = pos;
        lastPointerTime = Time.time;
    }
    public void OnPointerUp(Vector2 pos)
    {
        ray = mCamera.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out rayHit))
        {
            Vector3 hitPos = rayHit.point;
            mHandler.OnPointUp(pos, hitPos);
            if (Time.time - lastPointerTime < 0.35f && Vector3.Distance(pos, pointerDownPos) < 5)
            {
                mHandler.OnTap(pos, hitPos);
            }
        }        
       // StartCoroutine(DelayReset(0.2f));
    }


    public void OnPointerMove(Vector2 pixelPosition, Vector2 delta)
    {
        if (!isZooming && isCameraOn)
        {//拖拽
            if (delta.magnitude > 1)
            {
                Vector2 pp = mInputProvider.PointerPosition;
                if (!mHandler.OnDrag(pp))
                {
                    delta.y = -delta.y * factor;// * 0.2f;
                    delta.x = -delta.x * factor;// * 0.2f;
                    Vector3 cameraPosition = cameraTransform.localPosition;
                    cameraPosition.x += delta.x;// * Time.deltaTime;
                    cameraPosition.z += delta.y;// * Time.deltaTime;

                    cameraPosition = CheckBounds(cameraPosition);
                    cameraTransform.localPosition = cameraPosition;
                }
            }
        }
    }

    public void OnPointerZoom(Vector2 pixelPosition, float dis)
    {
        if (dis != 0 && !isZoomEffecting)
        {
            if (dis > 0 && dis > zoomFactor)// && v.y < 25.0f)
            {//缩小
                if (_curZoom < ZoomLevels.Length - 1)
                {
                    _curZoom++;
                    SetZoomLevel();
                    mHandler.OnZoom(_curZoom);
                }
            }
            else if (dis < 0 && dis < -zoomFactor )// && v.y > 3.0f)
            {//放大
                if (_curZoom > 0)
                {
                    _curZoom--;
                    SetZoomLevel();
                    mHandler.OnZoom(_curZoom);
                }
            }
            isZooming = true;
        }
    }
    bool isCameraOn = true;
    public void OnUpdate()
    {
        if (isControlled || mHandler == null || !mHandler.IsCameraOn())
        {
            if(isCameraOn)
                isCameraOn = false;
            return;
        }
        mInputProvider.Update();
        if (isZooming)
        {
            if (lastZoom != _curZoom)
            {
                lastZoom = _curZoom;
                ChangeCameraViewTo(ZoomLevels[_curZoom], 0.1f);
            }
            if (!isZoomEffecting)
            {
                isZooming = false;
                ResetBoundParams();
            }
        }
        if (!isCameraOn)
            isCameraOn = true;
    }

    /// ***************** 编辑用 ***********
    [SerializeField]
    private Vector3 paddingEdge = new Vector3(2,1.5f,1);
    [SerializeField]
    private bool isDbugMinZoom = true;
    [SerializeField]
    private bool isDebugSelfUpdate = false;
    [ContextMenu("ResetZoomLevels")]
    void ResetZoomLevels()
    {
        perLevelHeight = (maxHeight - minHeight) / 7.0f;
        ZoomLevels = new ZoomLevel[7];
        for (int i = 0; i < 7; i++)
        {
            ZoomLevel zl = new ZoomLevel();
            zl.Height = minHeight + i * perLevelHeight;
            ZoomLevels[i] = zl;
        }
        lastZoom = 1;
        _curZoom = isDbugMinZoom ? 0 : ZoomLevels.Length - 1;
        SetZoomLevel();
        mHandler.OnZoom(_curZoom);
        isZooming = true;
        if (isDebugSelfUpdate)
        {
            Invoke("ResetZoomLevels", 0.5f);
        }
    }
}
